#include "encodebuffer.h"
#include "encodebuffer2.h"
#include "decodebuffer.h"
#include "decodebuffer2.h"

#include <iostream>
#include <cassert>
#include <chrono>


template <typename T>
void test_coast_time(T func, const std::string &name)
{
    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
    func();
    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
    std::chrono::microseconds elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    std::cout << name << " Elapsed time: " << elapsed.count() << " microseconds" << std::endl;
}


void test_encodebuffer()
{
    uint8_t val1 = 0x12;
    uint16_t val2 = 0x3456;
    uint32_t val3 = 0x789ABCDE;
    uint64_t val4 = 0xFEDCBA9876543210;
    uint8_t val5[10] = {1,2,3,4,5,6,7,8,9,10};

    for (auto i = 0; i < 1e8; i++) {
        encodebuffer ef(1024);
        ef.append_u8(val1);
        ef.append_u16(val2);
        ef.append_u32(val3);
        ef.append_u64(val4);
        ef.append_buffer(val5, sizeof(val5));
        const uint8_t *ptr = ef.data();
        size_t len = ef.size();
    }
}

void test_encodebuffer2()
{
    uint8_t val1 = 0x12;
    uint16_t val2 = 0x3456;
    uint32_t val3 = 0x789ABCDE;
    uint64_t val4 = 0xFEDCBA9876543210;
    uint8_t val5[10] = {1,2,3,4,5,6,7,8,9,10};

    for (auto i = 0; i < 1e8; i++) {
        encodebuffer2 ef(1024);
        ef.append_u8(val1);
        ef.append_u16(val2);
        ef.append_u32(val3);
        ef.append_u64(val4);
        ef.append_buffer(val5, sizeof(val5));
        const uint8_t *ptr = ef.data();
        size_t len = ef.size();
    }
}

void test_array_encode() 
{
    uint8_t val1 = 0x12;
    uint16_t val2 = 0x3456;
    uint32_t val3 = 0x789ABCDE;
    uint64_t val4 = 0xFEDCBA9876543210;
    uint8_t val5[10] = {1,2,3,4,5,6,7,8,9,10};

    for (auto i = 0; i < 1e8; i++) {
        unsigned char buffer[1024];
        int pos = 0;
        buffer[0] = val1;
        pos += 1;
        *(uint16_t*)(buffer + pos) = htobe16(val2);
        pos += 2;
        *(uint32_t*)(buffer + pos) = htobe32(val3);
        pos += 4;
        *(uint64_t*)(buffer + pos) = htobe64(val4);
        pos += 8;
        memcpy(buffer + pos, val5, sizeof(val5));
        pos += sizeof(val5);
        const uint8_t *ptr = buffer;
        size_t len = pos;
    }
}


void test_decodebuffer()
{
    unsigned char buffer[] = {
        0x12,
        0x34, 0x56,
        0x78, 0x9A, 0xBC, 0xDE,
        0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A
    };
    size_t len = sizeof(buffer);

    for (auto i = 0; i < 1e8; i++) {
        decodebuffer df(buffer, len);
        df.reset();
        uint8_t val1 = 0;
        df.read_u8(val1);
        uint16_t val2 = 0;
        df.read_u16(val2);
        uint32_t val3 = 0;
        df.read_u32(val3);
        uint64_t val4 = 0;
        df.read_u64(val4);
        uint8_t val5[10] = {0};
        df.read_buffer(val5, sizeof(val5));
    }
}

void test_decodebuffer2()
{
    unsigned char buffer[] = {
        0x12,
        0x34, 0x56,
        0x78, 0x9A, 0xBC, 0xDE,
        0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A
    };
    size_t len = sizeof(buffer);


    for (auto i = 0; i < 1e8; i++) {
        decodebuffer2 df(buffer, len);
        df.reset();
        uint8_t val1 = 0;
        df.read_u8(val1);
        uint16_t val2 = 0;
        df.read_u16(val2);
        uint32_t val3 = 0;
        df.read_u32(val3);
        uint64_t val4 = 0;
        df.read_u64(val4);
        uint8_t val5[10] = {0};
        df.read_buffer(val5, sizeof(val5));
    }
}

void test_array_decode()
{
    unsigned char buffer[] = {
        0x12,
        0x34, 0x56,
        0x78, 0x9A, 0xBC, 0xDE,
        0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A
    };
    size_t len = sizeof(buffer);

    for (auto i = 0; i < 1e8; i++) {
        int pos = 0;
        uint8_t val1 = 0;
        val1 = buffer[pos];
        pos += 1;
        uint16_t val2 = 0;
        val2 = be16toh(*(uint16_t*)(buffer + pos));
        pos += 2;
        uint32_t val3 = 0;
        val3 = be32toh(*(uint32_t*)(buffer + pos));
        pos += 4;
        uint64_t val4 = 0;
        val4 = be64toh(*(uint64_t*)(buffer + pos));
        pos += 8;
        uint8_t val5[10] = {0};
        memcpy(val5, buffer + pos, sizeof(val5));
        pos += sizeof(val5);
    }
}


void test_encodebuffer_decodebuffer()
{
    encodebuffer ef(1024);
    uint8_t val1 = 0x12;
    uint16_t val2 = 0x3456;
    uint32_t val3 = 0x789ABCDE;
    uint64_t val4 = 0xFEDCBA9876543210;
    uint8_t val5[10] = {1,2,3,4,5,6,7,8,9,10};
    ef.append_u8(val1);
    ef.append_u16(val2);
    ef.append_u32(val3);
    ef.append_u64(val4);
    ef.append_buffer(val5, sizeof(val5));

    std::cout << "encode done, len: " << ef.size() << std::endl;

    const uint8_t *ptr = ef.data();
    size_t len = ef.size();

    for (size_t i = 0; i < len; ++i) {
        printf("%02X ", ptr[i]);
    }
    std::cout << std::endl;

    decodebuffer df(ptr, len);
    uint8_t val11 = 0;
    df.read_u8(val11);
    uint16_t val22 = 0;
    df.read_u16(val22);
    uint32_t val33 = 0;
    df.read_u32(val33);
    uint64_t val44 = 0;
    df.read_u64(val44);
    uint8_t val55[10] = {0};
    df.read_buffer(val55, sizeof(val55));

    std::cout << "decode done" << std::endl;

    // 检查读取的数据是否正确
    assert(val11 == val1);
    assert(val22 == val2);
    assert(val33 == val3);
    assert(val44 == val4);
    for (size_t i = 0; i < sizeof(val5); ++i) {
        assert(val55[i] == val5[i]);
    }

    std::cout << "test done" << std::endl;
}

int main(int argc, char const *argv[])
{
    test_coast_time(test_encodebuffer, "test_encodebuffer");
    test_coast_time(test_encodebuffer2, "test_encodebuffer2");
    test_coast_time(test_array_encode, "test_array_encode");

    std::cout << "============================" << std::endl;

    test_coast_time(test_decodebuffer, "test_decodebuffer");
    test_coast_time(test_decodebuffer2, "test_decodebuffer2");
    test_coast_time(test_array_decode, "test_array_decode");
    
    std::cout << "============================" << std::endl;

    test_encodebuffer_decodebuffer();

    return 0;
}

/*
-O1 -g
test_encodebuffer Elapsed time: 6631903 microseconds
test_encodebuffer2 Elapsed time: 3115659 microseconds
test_array_encode Elapsed time: 72744 microseconds
============================
test_decodebuffer Elapsed time: 2869026 microseconds
test_decodebuffer2 Elapsed time: 95585 microseconds
test_array_decode Elapsed time: 81658 microseconds

-O2 -g
test_encodebuffer Elapsed time: 3825364 microseconds
test_encodebuffer2 Elapsed time: 0 microseconds
test_array_encode Elapsed time: 0 microseconds
============================
test_decodebuffer Elapsed time: 1880168 microseconds
test_decodebuffer2 Elapsed time: 0 microseconds
test_array_decode Elapsed time: 0 microseconds

*/